import * as crypto from "crypto";

import log from "./log";
import { userModel } from "./mongo";
import * as Redis from "./redis";

export function md5(data: string) {
    return crypto.createHash("md5").update(data).digest("hex");
}

const redis = Redis.createClient(require("../../redisConfig.json"));

export class Auth {
    public async login(username: string, password: string, token: string): Promise<{ number: number; id: string; lastToken: string; }> {
        const result = {
            id: "",
            number: -1,
            lastToken: "",
        };
        try {
            const userInfo = await userModel.findOne({ username, number: { $gt: 0 } });
            if (!userInfo) {
                return result;
            }
            result.number = userInfo.number;
            if (md5(userInfo.password) === password) {
                await userModel.updateOne({ username }, { $set: { number: 5, token } })
                result.lastToken = await redis(0).get(`login_${ userInfo._id }`);
                result.id = userInfo._id;
                await redis(0).set(`login_${ userInfo._id }`, token);
            }
            await userModel.updateOne({ _id: userInfo._id }, { $inc: { number: -1 } });
        } catch (error) {
            log.app.info(error, username);
        }
        return result;
    }

    public async logout(token) {
        try {
            const userInfo = await userModel.findOne({ token });
            if (userInfo) {
                await redis(0).del(`login_${ userInfo._id }`);
            }
        } catch (error) {
            log.app.info(error, token);
        }
    }

    public async check(userId: string, token: string) {
        if (!userId) {
            throw new Error("noAuth");
        }
        console.log();
        const clientToken = await redis(0).get(`login_${ userId }`);
        if (clientToken !== token) {
            throw new Error("noAuth");
        }
    }
}

export function AuthController(target) {
    target.prototype.auth = new Auth();
}

export function CheckToken(target: any, name: string, descriptor: PropertyDescriptor) {
    const func = target[name];
    descriptor.value = async function (data) {
        await target.auth.check(data.id, data.token);
        return func.call(this, data);
    }
}